feat: api for shifting all relative past due dates#36499
Conversation
|
Thanks for the pull request, @kyrylo-kh! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
2a98593 to
4394424
Compare
4394424 to
68de318
Compare
68de318 to
fd5fc29
Compare
|
@e0d The |
|
|
||
| for course_key in course_keys: | ||
| try: | ||
| reset_deadlines_for_course(request, course_key, research_event_data) |
There was a problem hiding this comment.
Can you explain why the research_event_data is passed around here?
There was a problem hiding this comment.
This was in the original code and not introduced here. I think this is a bug and an anti-pattern. We should open a bug to fix this after this PR merges.
| ) | ||
| ) | ||
| @permission_classes((IsAuthenticated,)) | ||
| def reset_all_relative_course_deadlines(request): |
There was a problem hiding this comment.
The function names are changing unnecessarily across the three cases.
The original was/is:
def reset_course_deadlines(request)
The refactored helper is:
def reset_deadlines_for_course(request, course_key, research_event_data={})
The new build version is:
def reset_all_relative_course_deadlines(request)
I think we should have something like:
def reset_course_deadlines(request)
def reset_course_deadlines(request, course_key, research_event_data={})
def reset_all_course_deadlines(request)
I think it would probably be useful to have the core function of resetting in bulk be:
def reset_bulk_course_deadlines(coursekey_list)
This would make it available without passing in a request.
There was a problem hiding this comment.
I think we should have something like:
def reset_course_deadlines(request) def reset_course_deadlines(request, course_key, research_event_data={}) def reset_all_course_deadlines(request)
We can’t actually have both in the same file, because Python will just override the first with the second.
Did you mean:
- rename the view
reset_all_relative_course_deadlinestoreset_all_course_deadlines - rename the util
reset_deadlines_for_coursetoreset_bulk_course_deadlines(coursekey_list)?
Just want to confirm before refactoring, because right now reset_deadlines_for_course is request-aware (getting course_detail). I can leave request in parameters and use coursekey_list for iterating.
There was a problem hiding this comment.
Yes, you are right. I think that that naming convention you suggest makes sense. Ideally, I think, we wouldn't pass the request into the utility method because it couples it to the request cycle. However, this code is already messy because we're passing around the research event which is definitely non-standard, probably an anti-pattern.
9537e66 to
163c7d1
Compare
e0d
left a comment
There was a problem hiding this comment.
The latest changes address my comments. The test coverage issue still needs attention.
163c7d1 to
3c31cdb
Compare
| @@ -17,6 +21,11 @@ | |||
| reset_course_deadlines, | |||
| name='course-experience-reset-course-deadlines' | |||
| ), | |||
| re_path( | |||
There was a problem hiding this comment.
Nit: There's no reason for this to be an re_path just use the standard path which.
| @authentication_classes( | ||
| ( | ||
| JwtAuthentication, | ||
| BearerAuthenticationAllowInactiveUser, | ||
| SessionAuthenticationAllowInactiveUser, | ||
| ) | ||
| ) |
There was a problem hiding this comment.
Why do we need BearerAuthentication instead of just using the defalut JWT and Session authentication endpoints?
There was a problem hiding this comment.
I added it by analogy with a similar existing view (reset_course_deadlines). Additionally, mobile clients use bearer authorization, so BearerAuthenticationAllowInactiveUser is needed to support that alongside the default JWT and session authentication. For now it's used only on mobile application (bearer), but in future it also can be used on web (jwt+session)
| success_course_keys, failed_course_keys = reset_bulk_course_deadlines( | ||
| request, course_keys, research_event_data | ||
| ) |
There was a problem hiding this comment.
This seems like it could take a long time based on the size of the course list. Shouldn't this be a background(celery) task? Have we looked at the performance for 10,100,1000 courses and determined this is sufficiently fast?
There was a problem hiding this comment.
This runs pretty fast since it only updates existing schedules with a new start date - there’s no heavy logic or data creation, here is source code of main function which resets deadline:
def reset_self_paced_schedule(user, course_key, use_enrollment_date=False):
"""
Reset the user's schedule if self-paced.
It does not create a new schedule, just resets an existing one.
This is used, for example, when a user requests it or when an enrollment mode changes.
Arguments:
user (User)
course_key (CourseKey or str)
use_enrollment_date (bool): if False, reset to now, else reset to original enrollment creation date
"""
with transaction.atomic(savepoint=False):
try:
schedule = Schedule.objects.select_related('enrollment', 'enrollment__course').get(
enrollment__user=user,
enrollment__course__id=course_key,
enrollment__course__self_paced=True,
)
except Schedule.DoesNotExist:
return
if use_enrollment_date:
new_start_date = schedule.enrollment.created
else:
new_start_date = datetime.datetime.now(pytz.utc)
# Make sure we don't start the clock on the learner's schedule before the course even starts
new_start_date = max(new_start_date, schedule.enrollment.course.start)
schedule.start_date = new_start_date
schedule.save()Even if a user had a large number of courses, it should still complete quickly because each operation is just a small database update. Also, it’s very unlikely a single user would have hundreds of self-paced courses that all need to be reset.
|
@e0d looks like there’s an issue with the test environment - it’s the second time we’re seeing random test failures across different suites. |
Description
Introduce a new API endpoint to shift all relative past due dates. This endpoint processes all user enrollments and resets self-paced due dates accordingly. This functionality is required for the mobile application, where the "Dates" tab aggregates due dates from all courses. The app includes a button that allows users to shift all past due dates in one action.
The new endpoint is available at:
<LMS>/api/course_experience/v1/reset_all_course_deadlines/Testing instructions
<LMS>/api/course_experience/v1/reset_all_course_deadlines/with an empty request body. This API call will automatically update all past due dates for self-paced courses with relative week configurations, shifting them based on the current date.Deadline
None